from base_component import *
import os
from bear_helper import parse_compile_json


class BearGenerateIncludes(Component):
    """
        Component which generates all the include files
        required for compiling a particular file.
    """
    def __init__(self, value_dict):
        llvm_bc_out = None
        compiler_name = None
        compile_json = None
        kernel_src_dir = ""
        if 'llvm_bc_out' in value_dict:
            llvm_bc_out = value_dict['llvm_bc_out']
        if 'kernel_src_dir' in value_dict:
            kernel_src_dir = value_dict["kernel_src_dir"]
        if 'compile_json' in value_dict:
            compile_json = value_dict["compile_json"]

        self.kernel_src_dir = kernel_src_dir
        self.llvm_bc_out = llvm_bc_out
        self.compile_json = compile_json

    def setup(self):
        if self.llvm_bc_out is not None:
            if not os.path.exists(self.llvm_bc_out):
                os.makedirs(self.llvm_bc_out)
            if os.path.isfile(self.llvm_bc_out):
                return "Provided LLVM Bitcode out:" + self.llvm_bc_out + " is a file, but should be a directory path."
        else:
            return "LLVM Bitcode out should be provided."
        if self.compile_json is None or not os.path.exists(self.compile_json):
            return "No compile json or the provided file:" + str(self.compile_json) + " doesn't exist."
        return None

    def perform(self):
        return _generate_includes(self.kernel_src_dir, self.llvm_bc_out, self.compile_json)

    def get_name(self):
        return "BearGenerateIncludes"

    def is_critical(self):
        # No, this component is not critical.
        return False

# UTILITIES FUNCTION


def _get_src_file(build_args):
    """
        Get source file from the build command line.
    :param build_args: list of build args
    :return: file being compiled.
    """
    return build_args[-1]


def _split_includes(src_root_dir, src_file, build_options, output_folder):
    """
        Get LLVM build string from gcc build string
    :param src_root_dir: Directory containing all sources.
    :param src_file: Path to the source file.
    :param build_options: list of the build options.
    :param output_folder: folder where the .includes should be placed.
    """

    orig_build_args = build_options
    rel_src_file_name = src_file

    src_file_name = os.path.basename(rel_src_file_name)

    if str(rel_src_file_name).startswith("../"):
        rel_src_file_name = rel_src_file_name[3:]
    if str(rel_src_file_name).startswith('/'):
        rel_src_file_name = os.path.abspath(rel_src_file_name)
        if src_root_dir[-1] == '/':
            rel_src_file_name = rel_src_file_name[len(src_root_dir):]
        else:
            rel_src_file_name = rel_src_file_name[len(src_root_dir) + 1:]
    # replace output file with llvm bc file
    src_dir_name = os.path.dirname(rel_src_file_name)

    curr_output_dir = os.path.join(output_folder, src_dir_name)
    os.system('mkdir -p ' + curr_output_dir)

    curr_output_file = os.path.abspath(os.path.join(curr_output_dir, src_file_name[:-2] + '.includes'))
    fp_out = open(curr_output_file, 'w')
    # OK, now get all flags that start with -I and out them in curr_output_file
    for curr_flag in orig_build_args:
        curr_flag = curr_flag.strip()
        if str(curr_flag).startswith('-I'):
            fp_out.write(curr_flag + '\n')
    fp_out.close()


def _generate_includes(kernel_src_dir, base_output_folder, compile_json):
    """
    Generate includes from all compilation lines.
    :param kernel_src_dir: Directory containing the kernel sources.
    :param base_output_folder: output folder where all the includes file should be stored.
    :param compile_json: compile json generated by Bear
    :return: True/false depending on whether this step succeded or not.
    """
    comp_cmds, link_cmds = parse_compile_json(compile_json)
    for curr_comp_cmd in comp_cmds:
        build_args = curr_comp_cmd.curr_args
        src_file = curr_comp_cmd.src_file
        _split_includes(kernel_src_dir, src_file, build_args, base_output_folder)

    return True
